Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kbs: token: add verifier with JSON Web Keys #458

Merged
merged 2 commits into from
Aug 30, 2024

Conversation

mythi
Copy link
Contributor

@mythi mythi commented Aug 8, 2024

Add a new token verifier that uses JSON Web Keys (JWK) from
the configured JWK Set sources.

JWK Sets can be provided locally using file:// URL schema
or they can be downloaded automatically via OpenID Connect configuration
https:// URLs providing a pointer via "jwks_uri".

@zvonkok
Copy link
Member

zvonkok commented Aug 8, 2024

Can you add a meaningfull description? Thanks.

kbs/src/lib.rs Outdated Show resolved Hide resolved
kbs/src/token/coco.rs Outdated Show resolved Hide resolved
@mythi
Copy link
Contributor Author

mythi commented Aug 9, 2024

@Xynnn007 a bit offtopic but is there a reason why CoCo tokenverifier could not use jsonwebtoken also? AFAUI, we could have a single verifier that just understands local pem/jwks and OpenID Connect provided jwks:

trusted_cert_paths = ["/path/to/foo.pem", "https://provider.openid.conf", "/path/to/bar.jwks"]

@mythi mythi force-pushed the openid-token-verifier branch 2 times, most recently from 4c7b9bb to 52f2f06 Compare August 14, 2024 11:49
@mythi mythi marked this pull request as ready for review August 14, 2024 11:50
@mythi mythi changed the title JWKS token verifier using OpenID configuration kbs: token: add verifier with JSON Web Keys Aug 14, 2024
@mythi
Copy link
Contributor Author

mythi commented Aug 14, 2024

Can you add a meaningfull description? Thanks.

done

@Xynnn007
Copy link
Member

@Xynnn007 a bit offtopic but is there a reason why CoCo tokenverifier could not use jsonwebtoken also? AFAUI, we could have a single verifier that just understands local pem/jwks and OpenID Connect provided jwks:

I did not remember the concrete reason for it is a long time ago -- but I think it makes sense to use a common crate for the same functions.

@mythi
Copy link
Contributor Author

mythi commented Aug 15, 2024

@Xynnn007 a bit offtopic but is there a reason why CoCo tokenverifier could not use jsonwebtoken also? AFAUI, we could have a single verifier that just understands local pem/jwks and OpenID Connect provided jwks:

I did not remember the concrete reason for it is a long time ago -- but I think it makes sense to use a common crate for the same functions.

I was also wondering if it let us to drop dep:openssl.

@huoqifeng
Copy link

huoqifeng commented Aug 19, 2024

I was also wondering if it let us to drop dep:openssl.

For some verifier like IBMSE, openssl is the only certified cryptography library.

@mythi
Copy link
Contributor Author

mythi commented Aug 19, 2024

I was also wondering if it let us to drop dep:openssl.

For some verifier like IBMSE, openssl is the only certified cryptography library.

OK. I was mainly referring to the CoCo tokenverifier code.

@mythi
Copy link
Contributor Author

mythi commented Aug 19, 2024

@jialez0 PTAL

@mythi
Copy link
Contributor Author

mythi commented Aug 26, 2024

rebased + force pushed. however, this change is relatively independent and does not add any new dependencies. are there any concerns or reasons to wait with this?

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only two left comments.

}

pub async fn get_jwks_from_file_or_url(p: &str) -> Result<jwk::JwkSet, JwksGetError> {
match Url::parse(p) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not first

let url = Url::parse(p).map_err(...)?;
// then
match url {
...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the current match combines all checks under it. It is an error also when some insecure scheme is used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. A first impression is

// failed to parse
let url = Url::parse(p).map_err(...)?;

if url.scheme() == "https" {
    ...
}

if url.scheme() == "file" && Path::new(url.path()).exists() {
    ...
}

...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move the error check arm to be the first one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second Ding's original suggestion here. I would just get the error check out of the way in the beginning rather than adding another set of brackets. This will also allow you to return a more specific error.

I think it tends to be more readable to use match statements more like switch statements and to avoid chaining them together unless it really fits the fundamental logic of the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just updated this part too, thanks for the feedback!

@mythi
Copy link
Contributor Author

mythi commented Aug 26, 2024

Only two left comments.

what's the other one?

@Xynnn007
Copy link
Member

@mythi it is #458 (comment)

@mythi
Copy link
Contributor Author

mythi commented Aug 26, 2024

@mythi it is #458 (comment)

thanks, it's easy to miss comments in resolved conversations.

Copy link
Member

@fitzthum fitzthum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments on style but looks good overall.

kbs/src/token/jwk.rs Outdated Show resolved Hide resolved
}

pub async fn get_jwks_from_file_or_url(p: &str) -> Result<jwk::JwkSet, JwksGetError> {
match Url::parse(p) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second Ding's original suggestion here. I would just get the error check out of the way in the beginning rather than adding another set of brackets. This will also allow you to return a more specific error.

I think it tends to be more readable to use match statements more like switch statements and to avoid chaining them together unless it really fits the fundamental logic of the function.

_ => Some(keyset),
}
}
None => None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't use a match here since there is really no logic in this case. You can just use map instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. With the type change of trusted_certs_paths, we can simply write as

    pub async fn new(config: AttestationTokenVerifierConfig) -> Result<Self> {
        let keys = stream::iter(config.trusted_certs_paths)
            .filter_map(|p| async move {
                match get_jwks_from_file_or_url(&p).await {
                    Ok(jwkset) => Some(jwkset.keys),
                    Err(e) => {
                        warn!("error getting JWKS: {e:?}");
                        None
                    }
                }
            })
            .concat()
            .await;

        Ok(Self { trusted_certs: jwk::JwkSet { keys } })
    }

where futures_util crate is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I guess I blindly followed the CoCo approach so it ended up looking the same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where futures_util crate is needed.

the proposal looks nice but I wonder if this is justified for the functionality here?

Copy link
Member

@Xynnn007 Xynnn007 Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the stream::iter does, is try to do get_jwks_from_file_or_url concurrently rather than serially if multiple paths in trusted_certs_paths are given. This makes sense to me. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My use-case is with one URL only but I understand we may have several trusted_certs_path in the future where parallelism might help to improve the KBS startup time.

kbs/src/token/jwk.rs Outdated Show resolved Hide resolved
kbs/src/token/mod.rs Outdated Show resolved Hide resolved
_ => Some(keyset),
}
}
None => None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. With the type change of trusted_certs_paths, we can simply write as

    pub async fn new(config: AttestationTokenVerifierConfig) -> Result<Self> {
        let keys = stream::iter(config.trusted_certs_paths)
            .filter_map(|p| async move {
                match get_jwks_from_file_or_url(&p).await {
                    Ok(jwkset) => Some(jwkset.keys),
                    Err(e) => {
                        warn!("error getting JWKS: {e:?}");
                        None
                    }
                }
            })
            .concat()
            .await;

        Ok(Self { trusted_certs: jwk::JwkSet { keys } })
    }

where futures_util crate is needed.

kbs/src/token/jwk.rs Outdated Show resolved Hide resolved
@mythi mythi force-pushed the openid-token-verifier branch 2 times, most recently from 8e051d9 to a96ff5f Compare August 28, 2024 12:45
This is useful if any token verifier needs initialization data
pulled remotely.

Signed-off-by: Mikko Ylinen <[email protected]>
fitzthum
fitzthum approved these changes Aug 29, 2024
Copy link
Member

@fitzthum fitzthum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment on Ding's thread, but generally this seems fine.

@mythi mythi force-pushed the openid-token-verifier branch 2 times, most recently from 4c917b1 to 8d81645 Compare August 29, 2024 18:25
@fitzthum
Copy link
Member

Let's merge this if you are happy with it @Xynnn007

kbs/src/token/jwk.rs Outdated Show resolved Hide resolved
kbs/src/token/mod.rs Outdated Show resolved Hide resolved
Add a new token verifier that uses JSON Web Keys (JWK) from
the configured JWK Set sources.

JWK Sets can be provided locally using file:// URL schema
or they can be downloaded automatically via OpenID Connect configuration
URLs providing a pointer via "jwks_uri".

Signed-off-by: Mikko Ylinen <[email protected]>
@Xynnn007 Xynnn007 merged commit f6402d0 into confidential-containers:main Aug 30, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants